#!/usr/bin/env python
# -*- coding: utf-8 -*-

import cherrypy
import json
import datetime

from farado.logger import logger
from farado.ui.renderer import view_renderer
from farado.helpers.cookie_helper import current_session_id
from farado.general_manager_holder import project_manager, permission_manager
from farado.general_manager_holder import meta_item_manager, raw_querier
from farado.items.project import Project
from farado.items.version import Version
from farado.ui.operation_result import OperationResult
from farado.helpers.issue_column_helper import IssuesColumnHelper
from farado.permission_manager import PermissionFlag
from farado.ui.base_view import BaseView, UiUserRestrictions, DataTableArgs
from farado.ui.base_view import bring_value, get_value, get_int_value
from farado.editors.issue_editor import IssueEditor
from farado.editors.project_editor import ProjectEditor
from farado.editors.version_editor import VersionEditor
from farado.editors.editor_result_code import EditResultCode
from farado.database.raw_querier import intersect
from farado.helpers.issue_report_helper import IssueReportHelper



class ProjectsView(BaseView):

    #--------------------------------------------------------------------------#
    def __init__(self):
        super().__init__()
        self.name = '/projects'

    #--------------------------------------------------------------------------#
    @cherrypy.expose
    def index(self):
        user = self.current_user()
        if not user:
            return self.login_and_open()

        rights = self.project_rights(user.id)

        logger.info(f"%-18s | Открытие страницы с перечнем проектов.", user.login)

        return view_renderer["projects"].render(
            user=user,
            project_manager=project_manager(),
            operation_result=user.pop_last_action_result(),
            restriction=UiUserRestrictions(
                is_admin=self.is_admin(user.id),
                is_create_enabled=bool(PermissionFlag.creator <= rights),
                is_delete_enabled=bool(PermissionFlag.deleter <= rights),
            ),
        )

    #--------------------------------------------------------------------------#
    @cherrypy.expose
    def project(self, project_id=None, **args):
        user = self.current_user()
        if not user:
            if project_id:
                return self.login_and_open(f'/project/{project_id}', args)
            return self.login_and_open()

        args = bring_value(user.pop_last_action_data(), args)
        is_reading = bool(len(args) == 0)

        rights = self.project_rights(user.id, project_id)
        if PermissionFlag.watcher > rights:
            message = f"Нет доступа к проекту #{project_id}."
            logger.warn(f"%-18s | {message}", user.login)
            raise cherrypy.HTTPError(403, message)

        operation_result = user.pop_last_action_result()
        if is_reading:
            project = project_manager().project(project_id)
            if not project:
                message = f"Проект #{project_id} не найден."
                logger.warn(f"%-18s | {message}", user.login)
                raise cherrypy.HTTPError(404, message)
        else:
            result_code, result_text, project_id = ProjectEditor().change(
                rights,
                project_id,
                **args,
            )
            user.push_last_action_result(
                OperationResult(
                    caption=result_text,
                    result_code=result_code,
                )
            )
            project = project_manager().project(project_id)

            message = f"изменение проекта #{project_id}: {result_text}."
            if result_code:
                logger.info(f"%-18s | Удачное {message}", user.login)
            else:
                logger.warn(f"%-18s | Неудачное {message}", user.login)

            # NOTE Конструкция нужна при создании нового проекта, когда
            # target_project_id не известен, но нужно добавить его
            # в результирующий uri в адресной строке
            if EditResultCode.created == result_code:
                raise cherrypy.HTTPRedirect(
                    cherrypy.url(f'/project/{project.id}')
                )

        message = f"Открытие страницы проекта #{project_id}."
        logger.info(f"%-18s | {message}", user.login)

        return view_renderer["project"].render(
            user=user,
            target_project=project,
            project_manager=project_manager(),
            operation_result=operation_result,
            restriction=UiUserRestrictions(
                is_admin=self.is_admin(user.id),
                is_save_enabled=bool(PermissionFlag.editor <= rights),
            ),
            columns=project.get_issues_view_settings().split(','),
            column_helper=IssuesColumnHelper(),
        )

    #--------------------------------------------------------------------------#
    @cherrypy.expose
    def project_issues_data(self, project_id, version_id=None, **args):
        user = self.current_user()
        if not user:
            return '{}'

        # TODO: issue_kind_rights
        rights = self.project_rights(user.id)

        # Формирование перечня отфильтрованных и сортированных идентификаторов
        # запросов
        table_args = DataTableArgs(args)
        issues_column_helper = IssuesColumnHelper()
        user.push_long_action_handler(
            handler_caption='/project/project_issues_data',
            handler=issues_column_helper,
        )
        filtered_issues_ids = issues_column_helper.issues_ids_by_data_table_args(
            table_args=table_args,
        )
        if version_id:
            total_issues_ids = raw_querier().issues_ids_by_version_id(version_id)
        else:
            total_issues_ids = raw_querier().issues_ids_by_project_id(project_id)
        filtered_issues_ids = intersect(filtered_issues_ids, total_issues_ids)
        sliced_issues_ids = table_args.slice_ids(filtered_issues_ids)

        # Получение полного и отображаемого кол-ва запросов
        total_count = meta_item_manager().issues_count()
        filtered_count = len(filtered_issues_ids)

        # Получение экземпляров запросов
        issues = meta_item_manager().issues_by_ids(sliced_issues_ids)

        # Формирование целевых json данных для отображения в таблице
        data = []
        for issue in issues:
            column_helper = IssuesColumnHelper(issue)
            row = {}
            for column in table_args.columns:
                row.update(column_helper.prepare_column_data(column))
            data.append(row)

        result = {
            "draw": table_args.draw,
            "recordsTotal": total_count,
            "recordsFiltered": filtered_count,
            "data": data,
        }
        return json.dumps(result, indent=2)

    #--------------------------------------------------------------------------#
    @cherrypy.expose
    def add_project(self):
        user = self.current_user()
        if not user:
            return self.login_and_open('/add_project')

        rights = self.project_rights(user.id)
        if PermissionFlag.creator > rights:
            message = f"Нет доступа к созданию проекта."
            logger.warn(f"%-18s | {message}", user.login)
            raise cherrypy.HTTPError(403, message)

        logger.info(f"%-18s | Открытие страницы создания проекта.", user.login)

        return view_renderer["new_project"].render(
            user=user,
            project_manager=project_manager(),
            is_edit_mode=True,
            save_result=None,
            restriction=UiUserRestrictions(
                is_admin=self.is_admin(user.id),
                is_save_enabled=bool(PermissionFlag.editor <= rights),
            )
        )

    #--------------------------------------------------------------------------#
    @cherrypy.expose
    def remove_project(self, target_project_id):
        user = self.current_user()
        if not user:
            return self.login_and_open(f'/remove_project/{target_project_id}')

        rights = self.project_rights(user.id, target_project_id)
        if PermissionFlag.deleter > rights:
            message = f"Нет доступа к удалению проекта #{target_project_id}."
            logger.warn(f"%-18s | {message}", user.login)
            raise cherrypy.HTTPError(403, message)

        message = f"Проект #{target_project_id} удален."
        logger.info(f"%-18s | {message}", user.login)

        project_manager().remove_item(Project, target_project_id)
        user.push_last_action_result(
            OperationResult(
                caption="Проект удалён",
                result_code="success",
            )
        )
        raise cherrypy.HTTPRedirect(cherrypy.url(f'/'))

    #--------------------------------------------------------------------------#
    @cherrypy.expose
    def version(self, version_id=None, **args):
        user = self.current_user()
        if not user:
            if version_id:
                return self.login_and_open(f'/version/{version_id}', args)
            return self.login_and_open()

        args = bring_value(user.pop_last_action_data(), args)
        is_reading = bool(len(args) == 0)

        # TODO проверять права на проект из которого изымается версия и куда направляется
        version = project_manager().version(version_id)
        if version:
            project_id = version.project_id
        elif 'version_project_id' in args:
            project_id = args['version_project_id']

        rights = self.project_rights(user.id, project_id)
        if PermissionFlag.watcher > rights:
            message = f"Нет доступа к версии #{version_id} проекта."
            logger.warn(f"%-18s | {message}", user.login)
            raise cherrypy.HTTPError(403, message)

        operation_result = user.pop_last_action_result()
        if is_reading:
            version = project_manager().version(version_id)
            if not version:
                message = f"Версия #{version_id} не найдена."
                logger.warn(f"%-18s | {message}", user.login)
                raise cherrypy.HTTPError(404, message)
        else:
            result_code, result_text, version_id = VersionEditor().change(
                rights,
                version_id,
                **args,
            )
            user.push_last_action_result(
                OperationResult(
                    caption=result_text,
                    result_code=result_code,
                )
            )
            version = project_manager().version(version_id)

            message = f"изменение версии #{version_id} проекта: {result_text}."
            if result_code:
                logger.info(f"%-18s | Удачное {message}", user.login)
            else:
                logger.warn(f"%-18s | Неудачное {message}", user.login)

            # NOTE Конструкция нужна при создании новой версии проекта, когда
            # version_id не известен, но нужно добавить его в результирующий uri
            # в адресной строке
            if EditResultCode.created == result_code:
                raise cherrypy.HTTPRedirect(
                    cherrypy.url(f'/version/{version.id}')
                )

        message = f"Открытие страницы версии #{version_id} проекта #{version.project_id}."
        logger.info(f"%-18s | {message}", user.login)

        project = project_manager().project(version.project_id)
        return view_renderer["version"].render(
            user=user,
            project=project,
            version=version,
            project_manager=project_manager(),
            operation_result=operation_result,
            restriction=UiUserRestrictions(
                is_admin=self.is_admin(user.id),
                is_save_enabled=bool(PermissionFlag.editor <= rights),
            ),
            columns=project.get_issues_view_settings().split(','),
            column_helper=IssuesColumnHelper(),
        )

    #--------------------------------------------------------------------------#
    @cherrypy.expose
    def add_version(self, project_id):
        user = self.current_user()
        if not user:
            return self.login_and_open(f'/add_version/{project_id}')

        rights = self.project_rights(user.id, project_id)
        if PermissionFlag.creator > rights:
            message = f"Нет доступа к созданию версии проекта #{version.project_id}."
            logger.warn(f"%-18s | {message}", user.login)
            raise cherrypy.HTTPError(403, message)

        message = f"Открытие страницы создания версии проекта #{project_id}."
        logger.info(f"%-18s | {message}", user.login)

        return view_renderer["version"].render(
            user=user,
            is_edit_mode=True,
            project=project_manager().project(project_id),
            version=None,
            project_manager=project_manager(),
            restriction=UiUserRestrictions(
                is_admin=self.is_admin(user.id),
                is_save_enabled=bool(PermissionFlag.editor <= rights),
            ),
        )

    #--------------------------------------------------------------------------#
    @cherrypy.expose
    def remove_version(self, version_id):
        user = self.current_user()
        if not user:
            return self.login_and_open(f'/remove_version/{version_id}')

        version = project_manager().version(version_id)
        if not version:
            message = f"Версия #{version_id} не найдена."
            logger.warn(f"%-18s | {message}", user.login)
            raise cherrypy.HTTPError(404, message)

        project_id = version.project_id

        rights = self.project_rights(user.id, project_id)
        if PermissionFlag.deleter > rights:
            message = f"Нет доступа к удалению версии #{version_id} проекта."
            logger.warn(f"%-18s | {message}", user.login)
            raise cherrypy.HTTPError(403, message)

        logger.info(f"%-18s | Версия #{version_id} удалена.", user.login)
        project_manager().remove_item(Version, version_id)
        user.push_last_action_result(
            OperationResult(
                caption="Версия удалена",
                kind="success"
            )
        )
        raise cherrypy.HTTPRedirect(cherrypy.url(f'/project/{project_id}'))

    #--------------------------------------------------------------------------#
    @cherrypy.expose
    def archive_version_issues(self, version_id):
        user = self.current_user()
        if not user:
            return self.login_and_open(f'/archive_version_issues/{version_id}')

        version = project_manager().version(version_id)
        if not version:
            message = f"Версия #{version_id} не найдена."
            logger.warn(f"%-18s | {message}", user.login)
            raise cherrypy.HTTPError(404, message)

        project_id = version.project_id
        rights = self.project_rights(user.id, project_id)
        if PermissionFlag.deleter > rights:
            message = f"Нет доступа к удалению версии #{version_id} проекта."
            logger.warn(f"%-18s | {message}", user.login)
            raise cherrypy.HTTPError(403, message)

        archived_issues_quantity = 0
        editor = IssueEditor(user)
        issues = project_manager().filtered_issues(
            versions_ids=[version_id],
            states_ids=[
                state.id
                for state in project_manager().states()
                if state.is_finished() and not state.is_archive
            ],
        )
        for issue in issues:
            state = project_manager().state(issue.state_id)
            if not state:
                continue

            archive_states = project_manager().archive_states_by_workflow_id(
                workflow_id=state.workflow_id
            )
            if not archive_states:
                continue

            result_code, result_text, issue_id = editor.change(
                rights=rights,
                issue_id=issue.id,
                issue_state_id=str(archive_states[0].id),
            )

            message = f"архивирование запроса #{issue.id}: {result_text}."
            if result_code:
                logger.info(f"%-18s | Удачное {message}", user.login)
                archived_issues_quantity += 1
            else:
                logger.warn(f"%-18s | Неудачное {message}", user.login)
                has_errors = True

        total_quantity = len(issues)
        if 0 == total_quantity:
            message = f'Нет запросов для архивирования, версия #{version_id}.'
            logger.warn(f"%-18s | {message}", user.login)
            user.push_last_action_result(
                OperationResult(
                    caption="Архивирование версии",
                    text=message,
                    kind="warning"
                )
            )
        elif archived_issues_quantity == total_quantity:
            message = f'Архивирование версии #{version_id} выполнено успешно, '
            message += f'{archived_issues_quantity} из {total_quantity} '
            message += f'запросов обновлено.'
            logger.warn(f"%-18s | {message}", user.login)
            user.push_last_action_result(
                OperationResult(
                    caption="Архивирование версии",
                    text=message,
                    kind="success"
                )
            )
        else:
            message = f'Архивирование версии #{version_id} выполнено с ошибками, '
            message += f'{archived_issues_quantity} из {total_quantity} '
            message += f'запросов обновлено.'
            logger.warn(f"%-18s | {message}", user.login)
            user.push_last_action_result(
                OperationResult(
                    caption="Архивирование версии",
                    text=message,
                    kind="danger"
                )
            )

        raise cherrypy.HTTPRedirect(cherrypy.url(f'/version/{version_id}'))

    #--------------------------------------------------------------------------#
    @cherrypy.expose
    def report(self, project_id, **args):
        user = self.current_user()
        if not user:
            if project_id:
                return self.login_and_open(f'/report/{project_id}', args)
            return self.login_and_open()

        args = bring_value(user.pop_last_action_data(), args)

        from_date = f'{datetime.datetime.now():%d.%m.%Y}'
        to_date = from_date
        if date_range := get_value(args, 'daterange', None):
            date_range_container = date_range.split(" — ")
            from_date = date_range_container[0]
            to_date = date_range_container[1]

        rights = self.project_rights(user.id, project_id)
        if PermissionFlag.watcher > rights:
            message = f"Нет доступа к проекту #{project_id}."
            logger.warn(f"%-18s | {message}", user.login)
            raise cherrypy.HTTPError(403, message)

        project = project_manager().project(project_id)
        if not project:
            message = f"Проект #{project_id} не найден."
            logger.warn(f"%-18s | {message}", user.login)
            raise cherrypy.HTTPError(404, message)

        message = f"Формирование отчёта по проекту #{project_id} "
        message += f"за период {from_date} — {to_date}."
        logger.info(f"%-18s | {message}", user.login)

        issues_periods = IssueReportHelper().active_issues_ids_in_period_by_project(
            from_date=from_date,
            to_date=to_date,
            project_id=project_id,
        )

        return view_renderer["project"].render(
            user=user,
            target_project=project,
            project_manager=project_manager(),
            operation_result=OperationResult(
                caption='Отчёт сформирован',
                result_code='secondary',
                tab_name='report'
            ),
            restriction=UiUserRestrictions(
                is_admin=self.is_admin(user.id),
                is_save_enabled=bool(PermissionFlag.editor <= rights),
            ),
            columns=project.get_issues_view_settings().split(','),
            column_helper=IssuesColumnHelper(),
            report_data=issues_periods,
            from_date=from_date,
            to_date=to_date,
        )
